﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Interactivity;

namespace WPFTestSolution.Utils {
    
    /**
     * 解决 ScrollViewer 中嵌套 DataGrid, 当鼠标在 DataGrid 中会导致 ScrollViewer 不能滑动的问题.
     *
     * 
     * Bubbling scroll events from a ListView to its parent
     * https://stackoverflow.com/questions/1585462/bubbling-scroll-events-from-a-listview-to-its-parent
     *
     * If you're coming here looking for a solution to bubble the event ONLY if the child is at the top and scrolling up or the bottom and scrolling down, here's a solution.
     * I only tested this with DataGrid, but it should work with other controls as well.
     *
     * 
     * To attach this behavior, add the following XMLNS and XAML to your element, 使用:
     * xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
     * 
     * <ScrollViewer>
     *    <i:Interaction.Behaviors>
     *        <shared:ScrollParentWhenAtMax />
     *    </i:Interaction.Behaviors>
     * </ScrollViewer>
     *
     * todo: 待修改!
     */
    public class ScrollParentWhenAtMax : Behavior<FrameworkElement> {
        
        // protected override void OnAttached() {
        //     base.OnAttached();
        //     this.AssociatedObject.PreviewMouseWheel += PreviewMouseWheel;
        // }
        //
        // protected override void OnDetaching() {
        //     this.AssociatedObject.PreviewMouseWheel -= PreviewMouseWheel;
        //     base.OnDetaching();
        // }
        //
        // private void PreviewMouseWheel(object sender, MouseWheelEventArgs e) {
        //     Console.WriteLine($"sender = {sender}, e = {e}");
        //     var scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject);
        //     Console.WriteLine($"scrollViewer = {scrollViewer}");
        //     if (scrollViewer == null) return;
        //     double scrollPos = scrollViewer.ContentVerticalOffset;
        //     Console.WriteLine($"scrollPos = {scrollPos}, scrollViewer.ScrollableHeight = {scrollViewer.ScrollableHeight}");
        //     if (((int)scrollPos == (int) scrollViewer.ScrollableHeight && e.Delta < 0) || (scrollPos == 0 && e.Delta > 0)) {
        //         e.Handled = true;
        //         var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
        //         e2.RoutedEvent = UIElement.MouseWheelEvent;
        //         AssociatedObject.RaiseEvent(e2);
        //     }
        // }
        //
        // private static T GetVisualChild<T>(DependencyObject parent) where T : Visual {
        //     if (parent is T visual) return visual;
        //     
        //     T child = default(T);
        //
        //     int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        //     for (int i = 0; i < numVisuals; i++) {
        //         Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
        //         child = v as T;
        //         if (child == null) {
        //             child = GetVisualChild<T>(v);
        //         }
        //         if (child != null) {
        //             break;
        //         }
        //     }
        //     return child;
        // }
        
        
        protected override void OnAttached() {
            base.OnAttached();
            this.AssociatedObject.PreviewMouseWheel += PreviewMouseWheel;
        }
        
        protected override void OnDetaching() {
            this.AssociatedObject.PreviewMouseWheel -= PreviewMouseWheel;
            base.OnDetaching();
        }
        
        private void PreviewMouseWheel(object sender, MouseWheelEventArgs e) {
            if (e.Delta < 0) {
                var outerscroller = GetVisualParent<ScrollViewer>(this.AssociatedObject);
                if (outerscroller.ContentVerticalOffset < outerscroller.ScrollableHeight) {
                    e.Handled = true;
                    var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
                    e2.RoutedEvent = UIElement.MouseWheelEvent;
                    AssociatedObject.RaiseEvent(e2);
                }
            } else {
                var scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject);
                var scrollPos = scrollViewer.ContentVerticalOffset;
                if ((scrollPos == scrollViewer.ScrollableHeight && e.Delta < 0) || (scrollPos == 0 && e.Delta > 0)) {
                    e.Handled = true;
                    var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
                    e2.RoutedEvent = UIElement.MouseWheelEvent;
                    AssociatedObject.RaiseEvent(e2);
                }
            }
        }
        
        private static T GetVisualChild<T>(DependencyObject parent) where T : Visual {
            T child = default(T);
        
            int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < numVisuals; i++) {
                Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
                child = v as T;
                if (child == null) {
                    child = GetVisualChild<T>(v);
                }
                if (child != null) {
                    break;
                }
            }
            return child;
        }
        
        private static T GetVisualParent<T>(DependencyObject parent) where T : Visual {
            T obj = default(T);
            Visual v = (Visual)VisualTreeHelper.GetParent(parent);
            do {
                v = (Visual)VisualTreeHelper.GetParent(v);
                obj = v as T;
            } while (obj == null);
        
            return obj;
        }
    }
}